<?php
declare (strict_types = 1);

namespace app\api\controller;

use app\api\middleware\ImAuth;
use app\api\validate\Account as AccountValidate;
use app\common\facade\Token;
use app\common\model\Imapp;
use app\common\model\Imuser;
use ba\Random;
use think\facade\Config;
use think\facade\Db;
use think\Request;
use app\common\controller\Frontend;
use Throwable;

class Im extends Frontend
{
    public const TOKEN_TYPE = 'im';
    protected int $keepTime = 86400;
    protected int $refreshTokenKeepTime = 2592000;

    protected array $noNeedLogin = ['register','login','info','del','password'];
    protected array $middleware = [ImAuth::class];
    public function initialize(): void
    {
        parent::initialize(); // 调用父类的 initialize 方法
        $this->keepTime = (int)Config::get('buildadmin.user_token_keep_time');
    }
    public function index()
    {
        $this->success('success');
    }
    /**
     * 鉴权获取access_token
     * @param Request $request
     * @return void
     */
    public function token(Request $request): void
    {
        $param = $request->post();
        if(!isset($param['app_id']) || !isset($param['app_secret'])){
            $this->error('app_id or app_secret is empty');
        }
        $access_token = md5(Random::uuid());
        try {
            $find = Imapp::where(['app_id'=>$param['app_id'],'app_secret'=>$param['app_secret']])->findOrEmpty();
            if($find->isEmpty()){
                throw new \Exception('应用不存在或密钥错误');
            }
            Token::set($access_token, self::TOKEN_TYPE, $find->id,$this->keepTime);
        }catch (\Exception $e){
            $this->error($e->getMessage());
        }
        $data = [
            'access_token' => $access_token,
            'expires_in' => $this->keepTime,
            'token_type' => self::TOKEN_TYPE,
        ];
        $this->success('success', $data);
    }

    /**
     * 检查token
     * @param Request $request
     * @return array
     */
    public function checkToken(Request $request): array
    {
        $token = $request->header('AccessToken');
        if(!$token){
            $this->error('AccessToken is empty');
        }

        $tokenInfo = Token::get($token, self::TOKEN_TYPE);
        if(!$tokenInfo){
            $this->error('AccessToken is invalid');
        }
        if(!Token::check($token, self::TOKEN_TYPE, $tokenInfo['user_id'])){
            $this->error('AccessToken is invalid');
        }
        return $tokenInfo;
    }

    /**
     * 用户注册(用户批量注册)
     *
     * @param Request $request
     * @return void
     **
     * type 注册类型，支持 password 或 access_token
     */
    public function register(Request $request): void
    {
        //鉴权验证
        $app_id = $this->checkToken($request)['user_id'];
        Db::startTrans();
        try {
            $param = $request->post();
            if (empty($param['type'])) {
                throw new \Exception('type不能为空');
            }
            $users = $param['users'] ?? null;
            $users = $this->normalizeUsers($users);
            $this->validateUsers($users, $param['type']);

            //批量注册
            $successUsers = [];
            $failUsers = [];
            foreach ($users as $user){
                if(!isset($user['password'])){
                    $user['password'] = '';
                }
                $username_key = $user['username']."#{$app_id}";
                $find = \app\common\model\User::where('username',$username_key)->findOrEmpty();
                if(!$find->isEmpty()){
                    $user['fail_msg'] = '用户已存在';
                    unset($user['password']);
                    $failUsers[] = $user;
                    continue;
                }
                $res = $this->auth->register("appid_{$app_id}_".$user['username'],
                    password: $user['password'],
                    email: $user['username']."#{$app_id}".time()."@imapp.cn",
                    extend: ['nickname' => $user['username'],'username'=>$username_key]);
                unset($user['password']);
                if ($res === true) {
                    //绑定用户关系
                    $data = [
                        'user_id' => $this->auth->id,
                        'imapp_id' => $app_id
                    ];
                    Imuser::create($data);
                    $data['username_key'] = $username_key;
                    $data['token'] = $this->auth->getToken();
                    $data['id'] = $this->auth->id;
                    $data['success_msg'] = 'success';
                    $user = array_merge($user, $data);
                    $successUsers[] = $user;
                } else {
                    $msg = $this->auth->getError();
                    $msg = $msg ?: '注册失败，请重试或联系管理员';
                    $user['fail_msg'] = $msg;
                    $failUsers[] = $user;
                }
            }
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            $this->error($e->getMessage());
        }
        $this->success('success', [
            'success_num'=> count($successUsers),
            'fail_num'=> count($failUsers),
            'success_users' => $successUsers,
            'fail_users' => $failUsers
        ]);
    }
    /**
     * 标准化用户数据为二维数组
     */
    private function normalizeUsers($users): array
    {
        if (empty($users)) {
            throw new \Exception('users字段错误');
        }
        if (isset($users[0]) && is_array($users[0])) {
            if (count($users) > 100) {
                throw new \Exception('批量注册不能超过100个用户');
            }
            return $users;
        } elseif (is_array($users)) {
            return [$users];
        } else {
            throw new \Exception('users字段格式不正确');
        }
    }

    /**
     * 验证用户列表
     */
    private function validateUsers(array $users, string $type): void
    {
        $passwordRegex = '/^(?!.*[&<>"\'\n\r]).{6,32}$/';
        foreach ($users as $user) {
            if (empty($user['username'])) {
                throw new \Exception('username有空的值');
            }
            if ($type === 'password') {
                if (empty($user['password'])) {
                    throw new \Exception('password字段必须存在且不能为空');
                }
                if (!preg_match($passwordRegex, $user['password'])) {
                    throw new \Exception('password记录值必须在6到32个字符之间，并且不能包含 & < > " \' \n \r');
                }
            }
        }
    }

    /**
     * 用户 token 鉴权（账号+密码 or 账号+access_token）
     *
     * 账号+密码：无需使用access_token
     *
     * 账号+access_token：使用access_token
     * @param Request $request
     * @return void
     * @throws Throwable
     */
    public function login(Request $request): void
    {
        $param = $request->post();

        // 验证 type 参数
        $type = $param['type'] ?? '';
        if (!in_array($type, ['password', 'access_token'])) {
            $this->error('type参数错误');
        }

        // 获取 app_id
        $app_id = ($type === 'access_token')
            ? $this->checkToken($request)['user_id']
            : ($request->appId ?? null);

        if (!$app_id) {
            $this->error('无法获取 app_id');
        }
        $param['keep'] = true;
        try {
            $username_key = $param['username'] . "#{$app_id}";

            if ($type === 'password') {
                $res = $this->auth->login(
                    $username_key,
                    $param['password'] ?? '',
                    !empty($param['keep'])
                );
            } else {
                $user_id = \app\common\model\User::where('username', $username_key)->value('id');
                if (!$user_id) {
                    throw new \Exception('用户不存在');
                }
                $res = $this->auth->direct($user_id);
                $this->auth->setRefreshToken($this->refreshTokenKeepTime);
            }

        } catch (\Exception $e) {
            $this->error($e->getMessage());
        }

        // 登录结果处理
        if ($res === true) {
            $user = $this->auth->getUserInfo();
            $this->success(__('Login succeeded!'), [
                'id'=> $user['id'],
                'nickname'      => $user['nickname'],
                'username'      => $param['username'],
                'username_key'  => $user['username'],
                'token'         => $this->auth->getToken(),
                'avatar'        => full_url($user['avatar']),
                'gender'        => $user['gender'],
                'refresh_token' => $user['refresh_token'],
            ]);
        } else {
            $msg = $this->auth->getError() ?: '登录失败，请重试或联系管理员~';
            $this->error($msg);
        }
    }

    /**
     * 获取用户详情
     * @param Request $request
     * @return void
     */
    public function info(Request $request)
    {
        //鉴权验证
        $app_id = $this->checkToken($request)['user_id'];
        $username = $request->get('username');
        $id = $request->get('id');
        if(!$username and !$id){
            $this->error('用户名或ID不能全为空');
        }
        if($username){
            $username_key = $username . "#{$app_id}";
        }else{
            $username_key = $id;
        }
        try {
            $user = \app\common\model\User::where('username|id', $username_key)->find();
            if(!$user){
                throw new \Exception('用户不存在');
            }
            $info = $user->toArray();
            $info = array_intersect_key($info, array_flip($this->auth->getAllowFields()));
            $info['username_key'] = $info['username'];
            $info['username'] = explode('#',$info['username'])[0];
        }catch (\Exception $e){
            $this->error($e->getMessage());
        }
        $this->success('success',$info);
    }

    /**
     * 删除用户(批量删除用户)
     * @param Request $request
     * @return void
     */
    public function del(Request $request)
    {
        //鉴权验证
        $app_id = $this->checkToken($request)['user_id'];
        $usernames = $request->post('ids');
        if(!$usernames){
            $this->error('删除用户ID不能为空');
        }
        if(!is_array($usernames)){
            $usernames = [$usernames];
        }
        if(count($usernames) > 100){
            $this->error('删除用户单次不能超过100个');
        }
        Db::startTrans();
        try {
            $successUsers = [];
            $failUsers = [];
            foreach ($usernames as $username){
                $user = \app\common\model\User::where('id', $username)->find();
                if(!$user){
                    $failUsers[] = $username;
                    continue;
                }
                Imuser::where('user_id', $user->id)->where('imapp_id', $app_id)->delete();
                $user->delete();
                $successUsers[] = $username;
            }
            Db::commit();
        }catch (\Exception $e){
            Db::rollback();
            $this->error($e->getMessage());
        }
        $this->success('success',[
            'success_num'=>count($successUsers),
            'fail_num'=>count($failUsers),
            'success' => $successUsers,
            'fail' => $failUsers,
        ]);
    }

    public function password(Request $request)
    {
        //鉴权验证
        $app_id = $this->checkToken($request)['user_id'];
        $username = $request->get('username');
        $id = $request->get('id');
        $password = $request->post('password');
        if(!$username and !$id){
            $this->error('用户名或ID不能全为空');
        }
        if($username){
            $username_key = $username . "#{$app_id}";
        }else{
            $username_key = $id;
        }
        $user = \app\common\model\User::where('username|id', $username_key)->find();
        if(!$user){
            $this->error('用户不存在');
        }
        $this->auth->direct($user->id);
        $model = $this->auth->getUser();
        $model->startTrans();
        try {

            $validate = new AccountValidate();
            $validate->scene('changePassword')->check(['password' => $password]);
            $model->resetPassword($this->auth->id, $password);
            $model->commit();
        } catch (Throwable $e) {
            $model->rollback();
            $this->error($e->getMessage());
        }
        $this->auth->logout();
        $this->success('密码修改成功');

    }
}
